use vhdl_lang::{Config, MessagePrinter, Project};
use vhdl_lang::ast::*;
use std::path::Path;
use vhdl_lang::data;

use vhdl2s::vhdl::utils::*;
use vhdl2s::vhdl::spinal::*;
use vhdl2s::vhdl::units::*;

pub fn project_parse(config: &Config, print: &mut MessagePrinter) -> Project {
    Project::from_config(config, print)
}

pub fn dump(project: &Project, path: &Path) -> String {
    let mut output = String::new();
    let base = Database::from(&project);
    if let Some(design) = get_design(project,path) {
        let entities = entity(&design);
        for en in entities.clone() {
            let e = Entity::from(&en);
            output += &e.dumb_case_class(vec![String::new()]);
        }
        let arches = architecture(&design);
        for arch in arches {
            output += &format!("{:?}\n",arch.ident.name().name());
            output += &format!("{:?}\n",arch.entity_name.item.name().name());
            output += &instance_of_arch(&arch);
            output += &comp_io_of_arch(&arch);
            output += &dump_io_of_arch(&arch);
            output += &format!("{}",Architecture::from(&arch,&base).declare_instances());
        } 
    } 
    output
}

pub fn get_design(project: &Project, path: &Path) -> Option<DesignFile> {
    project.get_design(path)
}



pub fn interface2str(port:&InterfaceObjectDeclaration) -> String {
    let mut result = String::new();
    result += &format!("{:?} {:?}\n",port.ident.name(),port.mode);
    result += &format!("{}\n",subtype2spinal(&port.subtype_indication));
    if let Some(e) = port.expression.clone() {
        result += &format!("{:?}\n",e);
    }
    result
}
pub fn subtype2str(sub:&SubtypeIndication) -> String {
    let mut result = String::new();
    result += &signal2spinal(&to_str(&sub.type_mark));
    result += &format!("{}",sub.type_mark);
    if let Some(e) = sub.constraint.clone() {
        result += &constraint2str(e);
    }
    result += &format!("\n");
    result
}
pub fn constraint2str(con:data::WithPos<SubtypeConstraint>) -> String {
    let mut result = String::new();
    match con.item {
        SubtypeConstraint::Range(Range::Range(r)) => {
            result += &format!("{:?} {:?} {:?}",r.direction,r.left_expr,r.right_expr);
        },
        SubtypeConstraint::Array(v, ..) => {
            for item in v {
                match item {
                    DiscreteRange::Range(Range::Range(r)) => {
                        result += &range2str(&r);
                        // &format!("{:?} {:?} {:?}"
                        //     , r.direction
                        //     , r.left_expr.item
                        //     , r.right_expr.item
                        // );
                    },
                    _ => (),            
                }
            }
        }, 
        _ => (),
    }
    
    result    
}

pub fn range2str(r:&RangeConstraint) -> String {
    let dir = { match r.direction {
        Direction::Ascending =>  "to",
        Direction::Descending => "downto", 
    }};
    let left = r.left_expr.clone();
    let right = r.right_expr.clone();
    format!("( {} {} {} )",expression2str(left) , dir , expression2str(right))
}

pub fn expression2str(e:Box<data::WithPos<Expression>>) -> String {
    let mut result = String::new();
    match e.item {
        Expression::Binary(o,a,b) => {
            result += &format!("{:?} {:?} {:?}",expression2str(a),o,expression2str(b));
        },
        Expression::Unary(o,b) => {
            result += &format!("{:?} {:?}",o,expression2str(b));
        },
        Expression::Name(n) => {
            result += &format!("{:?}",n);
        },
        Expression::Literal(l) => {
            result += &literal2str(&l);
        },
        _ => (),
    }    
    result
}

pub fn literal2str(l:&Literal) -> String {
    match l {
        Literal::String(s)    => format!("{}",s),
        Literal::BitString(s) => format!("{}",s.value),
        Literal::Character(s) => format!("{}",s),
        Literal::AbstractLiteral(s) => match s {
            AbstractLiteral::Integer(s) => format!("{}",s),
            AbstractLiteral::Real(s) => format!("{}",s),
        },
        _ => String::new(),
    }
}
// pub fn type_mark2str(tm:&WithPos<SelectedName>) -> String {
//     format!("{}",tm)
// }
// pub fn interface2scala(port:&InterfaceObjectDeclaration) -> String {
//     let mut result = format!("var {} = ",port.ident.name());
//     match port.mode {
//         Mode::In    => {result += "in    ";},
//         Mode::Out   => {result += "out   ";},
//         Mode::InOut => {result += "inout ";},
//         _ => (),
//     }
//     subtype_indication
// }

// pub fn subtype2scala(sub:&SubtypeIndication) -> String {
//     match sub.type_mark {

//     }
// }

// pub struct SubtypeIndication {
//     pub resolution: ResolutionIndication,
//     pub type_mark: WithPos<SelectedName>,
//     pub constraint: Option<WithPos<SubtypeConstraint>>,
// }

